﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using SlimDX;
using SlimDX.Direct3D10;
using SlimDX.D3DCompiler;

namespace SGX
{ 
	public enum SourceLocation
	{
		File,
		Resource
	}

	public class TextureCache : IDisposable
	{
		public SourceLocation Location { get; set; }
		public string Source { get; set; }

        Texture2DDescription tex2DDesc;

        ShaderResourceView srv10;

        public TextureCache(Texture2DDescription desc)
        {
            tex2DDesc = desc;
            srv10 = null;
        }
        public ShaderResourceView SRV10
        {
            get { return srv10; }
        }
        public Texture2DDescription Description
        {
            get { return tex2DDesc; }
        }
        public void Dispose()
        {
            while(!srv10.Disposed)
                srv10.Dispose();
        }
	}

    public class FontCache : IDisposable
    {
        FontDescription fontDesc;
        Font font;

        public FontCache(FontDescription desc)
        {
            fontDesc = desc;
        }

        public Font Font10
        {
            get { return font; }
        }
        public FontDescription Description
        {
            get { return fontDesc; }
        }
        public void Dispose()
        {
            while(!font.Disposed)
                font.Dispose();
        }
    }

    public class EffectCache : IDisposable
    {
        public SourceLocation Location { get; set; }
        public string Source { get; set; }

        //SlimDX.D3DCompiler.ShaderFlags;
        public EffectCache(string srcFile, Effect effect)
        {
            Source = srcFile;
            this.effect = effect;
            Location = SourceLocation.File;
        }

        Effect effect;

        public EffectCache()
        {
        }

        public Effect Effect10
        {
            get { return effect; }
        }

        public void Dispose()
        {
            while(!effect.Disposed)
                effect.Dispose();
        }
    }

    public class GlobalResourceCache : IDisposable
    {
        static GlobalResourceCache rc;
        static Device device;
        private GlobalResourceCache() 
        {
            textureCache = new Dictionary<string, TextureCache>();
            fontCache = new Dictionary<string, FontCache>();
            effectCache = new Dictionary<string, EffectCache>();
            effectPoolCache = new Dictionary<string, EffectPool>();
            rasterizerCache = new Dictionary<RasterizerStateDescription, RasterizerState>();
            depthStencilCache = new Dictionary<DepthStencilStateDescription, DepthStencilState>();
            blendCache = new Dictionary<BlendStateDescription, BlendState>();

            device = null;
        }
        static public void InitResourceCache(Device device)
        {
            if (rc == null)
                rc = new GlobalResourceCache();
            GlobalResourceCache.device = device;
        }
        static public GlobalResourceCache ResourceCache
        {
            get
            {
                return rc;
            }
        }
        static public Device Device { get { return device; } }

        public static ShaderResourceView CreateTextureFromFile(string srcFile, string resourceName)
        {
            throw new NotImplementedException("createTextureFromFile not implemented");
        }

        public static Effect CreateEffectFromFile(string srcFile, out string compilationErrors)
        {
            ShaderFlags sflags = ShaderFlags.EnableStrictness;
#if DEBUG
            sflags |= ShaderFlags.Debug;
#endif
            string fullPath = System.IO.Path.GetFullPath(srcFile);
            var cacheDict = GlobalResourceCache.ResourceCache.effectCache;
            EffectCache cache;
            bool result = cacheDict.TryGetValue(fullPath, out cache);
            
            if (result == true)
            {
                Effect e = Effect.FromPointer(cache.Effect10.ComPointer);
                compilationErrors = "";
                return e;
            }

            Effect effect = Effect.FromFile(device, srcFile, "fx_4_1", sflags, EffectFlags.None, null, null, null, out compilationErrors);
            if(effect != null)
                cacheDict.Add(fullPath, new EffectCache(fullPath, effect));
            return effect;
        }

        public static EffectPool CreateEffectPoolFromFile(string srcFile, out string compilationErrors)
        {
            ShaderFlags sflags = ShaderFlags.EnableStrictness;
#if DEBUG
            sflags |= ShaderFlags.Debug;
#endif
            string fullPath = System.IO.Path.GetFullPath(srcFile);
            var cacheDict = GlobalResourceCache.ResourceCache.effectPoolCache;
            EffectPool pool;
            bool result = cacheDict.TryGetValue(fullPath, out pool);

            if (result == true)
            {
                var e = EffectPool.FromPointer(pool.ComPointer);
                compilationErrors = "";
                return e;
            }

            pool = EffectPool.FromFile(device, srcFile, "fx_4_1", sflags, EffectFlags.None, out compilationErrors);
            if (pool != null)
                cacheDict.Add(fullPath, pool);
            return pool;
        }

        public static Effect CreateChildEffectFromFile(string srcFile, EffectPool pool, out string compilationErrors)
        {
            ShaderFlags sflags = ShaderFlags.EnableStrictness;
#if DEBUG
            sflags |= ShaderFlags.Debug;
#endif
            string fullPath = System.IO.Path.GetFullPath(srcFile);
            var cacheDict = GlobalResourceCache.ResourceCache.effectCache;
            EffectCache cache;
            bool result = cacheDict.TryGetValue(fullPath, out cache);

            if (result == true && cache.Effect10.Description.IsChildEffect)
            {
                Effect e = Effect.FromPointer(cache.Effect10.ComPointer);
                compilationErrors = "";
                return e;
            }

            Effect effect = Effect.FromFile(device, srcFile, "fx_4_1", sflags, EffectFlags.ChildEffect, null, null, null, out compilationErrors);
            if (effect != null)
                cacheDict.Add(fullPath, new EffectCache(fullPath, effect));
            return effect;
        }

        public static DepthStencilState CreateDepthStencilState(DepthStencilStateDescription desc)
        {
            DepthStencilState state;
            var dict = GlobalResourceCache.ResourceCache.depthStencilCache;

            bool result = dict.TryGetValue(desc, out state);
            if (result == true)
            {
                state = DepthStencilState.FromPointer(state.ComPointer);
                return state;
            }

            state = DepthStencilState.FromDescription(device, desc);
            if (state != null)
                dict.Add(desc, state);
            return state;
        }

        public static RasterizerState CreateRasterizerState(RasterizerStateDescription desc)
        {
            RasterizerState state;
            var dict = GlobalResourceCache.ResourceCache.rasterizerCache;

            bool result = dict.TryGetValue(desc, out state);
            if (result == true)
            {
                state = RasterizerState.FromPointer(state.ComPointer);
                return state;
            }

            state = RasterizerState.FromDescription(device, desc);
            if (state != null)
                dict.Add(desc, state);
            return state;
        }

        public static BlendState CreateBlendState(BlendStateDescription desc)
        {
            BlendState state;
            var dict = GlobalResourceCache.ResourceCache.blendCache;

            bool result = dict.TryGetValue(desc, out state);
            if (result == true)
            {
                state = BlendState.FromPointer(state.ComPointer);
                return state;
            }

            state = BlendState.FromDescription(device, desc);
            if (state != null)
                dict.Add(desc, state);
            return state;
        }

        Dictionary<string, TextureCache> textureCache;
        Dictionary<string, FontCache> fontCache;
        Dictionary<string, EffectCache> effectCache;
        Dictionary<string, EffectPool> effectPoolCache;
        Dictionary<DepthStencilStateDescription, DepthStencilState> depthStencilCache;
        Dictionary<RasterizerStateDescription, RasterizerState> rasterizerCache;
        Dictionary<BlendStateDescription, BlendState> blendCache;

        public void Dispose()
        {
            foreach (var e in effectCache)
            {
                e.Value.Dispose();
            }
            foreach (var t in textureCache)
            {
                t.Value.Dispose();
            }
            foreach (var f in fontCache)
            {
                f.Value.Dispose();
            }
            foreach (var e in effectPoolCache)
            {
                while(!e.Value.Disposed)
                    e.Value.Dispose();
            }
            foreach (var e in rasterizerCache)
            {
                while (!e.Value.Disposed)
                    e.Value.Dispose();
            }
            foreach (var e in depthStencilCache)
            {
                while (!e.Value.Disposed)
                    e.Value.Dispose();
            }
            foreach (var e in blendCache)
            {
                while (!e.Value.Disposed)
                    e.Value.Dispose();
            }
        }
    }
}